package org.totoro.classload;

import lombok.SneakyThrows;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * 加载，验证，准备，解析，初始化五个
 * 自定义类加载器
 *
 * @author daocr
 * @date 2020/3/11
 */
public class CustomerClassLoad {

    public static final String CLASS_NAME = "org.totoro.classload.Code";

    public static void main(String[] args) throws ClassNotFoundException {

        MyClassLoadOverrideLoadClass myClassLoadOverrideLoadClass = new MyClassLoadOverrideLoadClass();
        Class<?> loadClass = myClassLoadOverrideLoadClass.loadClass(CLASS_NAME);

        System.out.println("类加器: " + loadClass.getClassLoader() + "类 hashcode :" + loadClass.hashCode());

        MyClassLoadOverrideFindClass myClassLoadOverrideFindClass = new MyClassLoadOverrideFindClass();
        Class<?> findClass = myClassLoadOverrideFindClass.loadClass(CLASS_NAME);
        Class<?> findClass2 = myClassLoadOverrideFindClass.loadClass(CLASS_NAME);

        System.out.println("类加器: " + findClass.getClassLoader() + "类 hashcode :" + findClass.hashCode());

        System.out.println("不同类加载，加载的类是否在同一个内存" + (loadClass == findClass));
        System.out.println("同一个类，加载同一个类到内存，地址是否一样" + (findClass == findClass2));

    }


    public static class MyClassLoadOverrideFindClass extends ClassLoader {


        /**
         * 只有父类加载器找不到的时候，才会执行自己的加载方法，遵守双亲委派机制
         *
         * @param name
         * @return
         * @throws ClassNotFoundException
         */
        @SneakyThrows
        @Override
        protected Class<?> findClass(String name) throws ClassNotFoundException {

            byte[] classCode = CustomerClassLoad.getClassCode();

            return super.defineClass(CLASS_NAME, classCode, 0, classCode.length);

        }

    }


    /**
     * 自定义类加载器 ，尽量不要重写改方法
     */
    public static class MyClassLoadOverrideLoadClass extends ClassLoader {

        /**
         * 重写 load class 破坏了双亲委派机制
         *
         * @param name
         * @return
         * @throws ClassNotFoundException
         */
        @SneakyThrows
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {

            try {
                // 实现双亲委派，父类先加载，如果找不到，我们再去加载
                return super.loadClass(name);
            } catch (ClassNotFoundException e) {
                byte[] classCode = CustomerClassLoad.getClassCode();
                System.out.println(name);
                return super.defineClass(name, classCode, 0, classCode.length);
            }
        }

    }

    private static byte[] getClassCode() throws IOException {
        URL resource = CustomerClassLoad.class.getResource("/lib/Code.class");
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Files.copy(Paths.get(resource.getPath()), byteArrayOutputStream);
        byteArrayOutputStream.close();

        byte[] bytes = byteArrayOutputStream.toByteArray();

        return bytes;
    }

}
